// ****************************************************************************
//	Copyright (c) 2011 Daniel Watman
//	http://www.keycommander.net/
// ****************************************************************************
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//	
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//	
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
// ****************************************************************************
//	File: btn_test.c
//	Purpose: test of conversion of PNG images to LCD image format
//	Author:	Daniel Watman
//	Created: 2011.03.03
//	Updated: 2011.03.03
//	Version: 1.0
//	Target:	linux (hopefully only minor modifications for libusb-win32)
//	Compiler: GCC
// ****************************************************************************
//	Changes:
//		1.0	* Initial version. Basic IN/OUT test works
// ****************************************************************************
//	To do:
//		* Check if it works in windows
// ****************************************************************************
//	Comments:
//		* This program uses libusb-1.0. Compile with -L/usr/local -lusb-1.0
// ****************************************************************************

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <errno.h> 
#include <libusb-1.0/libusb.h> 

#define LINUX 1		// for OS specific things

#define VENDOR_ID 0x0B6A  	// Maxim VID and PID for now
#define PRODUCT_ID 0x5346

#define INTERFACE	0		// interface to test
#define LEN_INT_OUT	8		// length of OUT packet
#define LEN_INT_IN	8		// length of IN packet
#define EP_INT_IN	0x83	// endpoint IN address (3)
#define EP_INT_OUT	0x01	// endpoint OUT address (1)
#define TIMEOUT		5000	// 5 sec timeout

void hr_err(int);	// human readable errors

int main(void) { 
	static struct libusb_device_handle *devh = NULL;  
	int err; 
	int configval;
	int was_attached = 0;
	unsigned char TXbuf[LEN_INT_OUT] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
	unsigned char RXbuf[LEN_INT_IN];
	int TXlen, RXlen;

	err = libusb_init(NULL); 
	if (err < 0) { fprintf(stderr, "Failed to initialise libusb\n"); exit(1); } 

	devh = libusb_open_device_with_vid_pid(NULL, VENDOR_ID, PRODUCT_ID);
	if (devh == NULL) { fprintf(stderr, "Could not find/open the HID device\n"); goto quit; } 
	printf("Successfully located the HID device\n"); 

	#ifdef LINUX
	if (libusb_kernel_driver_active(devh, INTERFACE)==1) {
		err = libusb_detach_kernel_driver(devh, INTERFACE);
		if (err < 0) { fprintf(stderr, "libusb_detach_kernel_driver error %d ", err); hr_err(err);} 
		was_attached = 1;
		printf("Detached from kernel driver\n");
	}
	else printf("Kernel driver not active\n");
	#endif

	err = libusb_get_configuration(devh, &configval); 
	if (err < 0) { fprintf(stderr, "libusb_get_configuration error %d ", err); hr_err(err); goto quit; }
	else printf("Configuration #%d\n", configval);

	if (configval < 1) {	// only attempt to configure if unconfigured
		err = libusb_set_configuration(devh, 1); 
		if (err < 0) { fprintf(stderr, "libusb_set_configuration error %d ", err); hr_err(err); goto quit; } 
		else printf("Successfully changed to configuration #1\n"); 
	}

	err = libusb_claim_interface(devh, INTERFACE); 
	if (err < 0) { fprintf(stderr, "libusb_claim_interface error %d ", err); hr_err(err); goto quit; } 
	else printf("Successfully claimed interface %u\n", INTERFACE); 

	printf("\tTesting interrupt OUT transfer...\n"); 
	err = libusb_interrupt_transfer(devh, EP_INT_OUT, TXbuf, LEN_INT_OUT, &TXlen,TIMEOUT); 
	if (err < 0) { fprintf(stderr, "Interrupt write error %d ", err); hr_err(err); } 
	else printf("Interrupt write test successful\n");

	printf("\tTesting interrupt IN transfer...\n"); 
	err = libusb_interrupt_transfer(devh, EP_INT_IN, RXbuf,LEN_INT_IN, &RXlen, TIMEOUT); 
	if (err < 0) { fprintf(stderr, "Interrupt read error %d ", err); hr_err(err); } 
	else {
		unsigned char i;
		printf("Interrupt read test successful:\t");
		for (i=0;i<RXlen;i++)
			printf("%02X ", RXbuf[i]);
		printf("\n");
	}

	quit: 
	if (devh != NULL) libusb_release_interface(devh, INTERFACE); 
	
	//libusb_reset_device(devh);	// not needed?
	#ifdef LINUX
	if (was_attached == 1)	// reattach to kernel driver if it was detached at the start
		err = libusb_attach_kernel_driver(devh, INTERFACE);
		if (err < 0) { fprintf(stderr, "ibusb_attach_kernel_driver error %d ", err); hr_err(err);} 
		else printf("Re-attached to kernel driver\n");
	#endif
	
	libusb_close(devh);
	libusb_exit(NULL);
	return err;
} 

// libusb error codes
void hr_err(int errcode) {
	switch (errcode) {
		case -1:	printf("(IO error)\n"); break;
		case -2:	printf("(invalid parameter)\n"); break;
		case -3:	printf("(access error)\n"); break;
		case -4:	printf("(no device)\n"); break;
		case -5:	printf("(not found)\n"); break;
		case -6:	printf("(busy)\n"); break;
		case -7:	printf("(timeout)\n"); break;
		case -8:	printf("(overflow)\n"); break;
		case -9:	printf("(pipe error)\n"); break;
		case -10: printf("(interrupted)\n"); break;
		case -11: printf("(no memory)\n"); break;
		case -12: printf("(not supported)\n"); break;
		default: printf("(other error)\n"); break;
	}
}
